package example;

import framework.BoardInterface;
import framework.CellImpl;
import framework.CellInterface;

/**
 * A sample 50x50 board with initial cell states that correspond to the
 * GosplerGliderGun.
 * 
 */
public class Board implements BoardInterface {
	// Set size of board to 50x50
	private final static int width = 50;
	private final static int height = 50;
	// The number of neighbors of each cell is 8
	private final static int numNbrs = 8;
	// The 2 states: dead state represented as 0 and live state represented
	// as 1
	private final static int STATE_DEAD = 0;
	private final static int STATE_LIVE = 1;
	// Cell array to store all cell information
	private CellInterface[] cellArray = new CellInterface[width * height];

	/**
	 * Constructor that creates a cell at each position on board and sets the x
	 * and y coordinates of the cells
	 */
	public Board() {
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				CellInterface c = new CellImpl();
				c.setColNum(j);
				c.setRowNum(i);
				c.setState(STATE_DEAD);
				cellArray[i * width + j] = c;
			}
		}
	}

	/**
	 * Sets initial states of cells. This sample draws the GosplerGliderGun
	 */
	private void drawGosperGliderGun() {
		cellArray[1 * width + 24].setState(STATE_LIVE);
		cellArray[2 * width + 22].setState(STATE_LIVE);
		cellArray[2 * width + 24].setState(STATE_LIVE);
		cellArray[3 * width + 20].setState(STATE_LIVE);
		cellArray[3 * width + 21].setState(STATE_LIVE);
		cellArray[4 * width + 20].setState(STATE_LIVE);
		cellArray[4 * width + 21].setState(STATE_LIVE);
		cellArray[5 * width + 20].setState(STATE_LIVE);
		cellArray[5 * width + 21].setState(STATE_LIVE);
		cellArray[7 * width + 24].setState(STATE_LIVE);
		cellArray[6 * width + 24].setState(STATE_LIVE);
		cellArray[6 * width + 22].setState(STATE_LIVE);

		cellArray[5 * width + 10].setState(STATE_LIVE);
		cellArray[6 * width + 10].setState(STATE_LIVE);
		cellArray[7 * width + 10].setState(STATE_LIVE);
		cellArray[4 * width + 11].setState(STATE_LIVE);
		cellArray[8 * width + 11].setState(STATE_LIVE);
		cellArray[3 * width + 12].setState(STATE_LIVE);
		cellArray[9 * width + 12].setState(STATE_LIVE);
		cellArray[3 * width + 13].setState(STATE_LIVE);
		cellArray[9 * width + 13].setState(STATE_LIVE);
		cellArray[6 * width + 14].setState(STATE_LIVE);
		cellArray[4 * width + 15].setState(STATE_LIVE);
		cellArray[8 * width + 15].setState(STATE_LIVE);
		cellArray[5 * width + 16].setState(STATE_LIVE);
		cellArray[6 * width + 16].setState(STATE_LIVE);
		cellArray[7 * width + 16].setState(STATE_LIVE);
		cellArray[6 * width + 17].setState(STATE_LIVE);

		cellArray[5 * width + 0].setState(STATE_LIVE);
		cellArray[6 * width + 0].setState(STATE_LIVE);
		cellArray[5 * width + 1].setState(STATE_LIVE);
		cellArray[6 * width + 1].setState(STATE_LIVE);

		cellArray[3 * width + 35].setState(STATE_LIVE);
		cellArray[4 * width + 35].setState(STATE_LIVE);
		cellArray[3 * width + 34].setState(STATE_LIVE);
		cellArray[4 * width + 34].setState(STATE_LIVE);
	}

	/**
	 * Returns all cells on board.
	 * 
	 * @return CellInterface[] cellArray - all cells on the board
	 */
	@Override
	public CellInterface[] getCells() {
		return cellArray;
	}

	/**
	 * Returns the total number of columns in the board, if the board is viewed
	 * as a 2d array.
	 * 
	 * @return int width - the total number of columns
	 */
	@Override
	public int getTotalColNum() {
		return width;
	}

	/**
	 * Returns the total number of rows in the board, if the board is viewed as
	 * a 2d array.
	 * 
	 * @return int height - the total number of rows
	 */
	@Override
	public int getTotalRowNum() {
		return height;
	}

	/**
	 * Returns the number of neighbors a cell has.
	 * 
	 * @return int numNbrs - the number of neighbors a cell has
	 */
	@Override
	public int getNbrNum() {
		// This sample take cell's 8 surrounding cells as its neighbors
		return numNbrs;
	}

	/**
	 * 
	 * Given a cell, this method returns its neighbors. Definition of what
	 * constitutes a neighbor is client-defined. This sample takes the
	 * surrounding 8 cells as neighbors.
	 * 
	 * @param CellInterface c - a cell
	 * @return CellInterface[] neighborArray - all the neighbors of c
	 */
	@Override
	public CellInterface[] getNeighbors(CellInterface c) {
		// In this sample, each cell has 8 neighbors
		CellInterface[] neighborArray = new CellInterface[numNbrs];

		int col = c.getColNum();
		int row = c.getRowNum();

		// When considering the 8 neighbors, we look at the a 3x3 block of cells
		// with the outer 8 cells being the neighbors
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				int idx = i * 3 + j < 4 ? i * 3 + j : i * 3 + j - 1;
				if (i != 1 || j != 1) {
					int r1 = row + (i - 1);
					int c1 = col + (j - 1);
					if (0 <= r1 && r1 < height && 0 <= c1 && c1 < width) {
						neighborArray[idx] = cellArray[(r1) * width + c1];
					}
				}
			}
		}
		return neighborArray;
	}

	/**
	 * Cleans the board by setting all cell states to the "empty" state, where
	 * "empty" is client-defined.
	 */
	@Override
	public void cleanBoard() {
		for (CellInterface c : cellArray) {
			// This sample uses 0 as the empty state
			c.setState(STATE_DEAD);
		}
	}

	/**
	 * Set board to "initial" state, where "initial" state is client-defined.
	 * Framework will call this method when initializing and re-initializing
	 * board. Re-initializing is done when user selects the "reset" button.
	 */
	@Override
	public void initBoard() {
		// This sample draws the GosperGliderGun as the initial state.
		drawGosperGliderGun();
	}

	/**
	 * Returns the cell at position (row, col) if board is viewed as a 2d array.
	 * 
	 * @param int row - the row of the target cell
	 * @param int col - the column of the target cell
	 * @return CellInterface c - the cell at (row, col) in the board
	 */
	@Override
	public CellInterface getTargetCell(int row, int col) {
		return cellArray[row * width + col];
	}
}
